#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace GameFramework.AudioSystem
{
    /// <summary>
    /// Simple AudioCategory wrapper providing some extended functionality 
    /// that hides the support code for the AudioManager volume enum, and
    /// public property and method access.
    /// </summary>
    public class CategoryObject
    {
        #region Fields

        /// <summary>
        /// AudioCategory object field storage
        /// </summary>
        private AudioCategory categoryObject;

        /// <summary>
        /// AudioCategory state fields to track Muted and Volume category status.
        /// </summary>
        private bool    categoryMuted   = false;
        private Volume  categoryVolume  = Volume.Full;

        #endregion

        #region Properties

        /// <summary>
        /// Get/Set the internal volume property
        /// </summary>
        public Volume Volume
        {
            get { return categoryVolume; }
            set 
            {
                // Set the current sound volume for all sound cues that have been
                // associated with this category through the XACT Tool and set the
                // current category volume state field.
                categoryObject.SetVolume( CalculateVolume(value) );
                categoryVolume = value;
            }
        }

        /// <summary>
        /// Get a physical copy of the AudioCategory storage.
        /// </summary>
        public AudioCategory Category
        {
            get { return categoryObject; }
        }

        #endregion

        #region Construction

        /// <summary>
        /// Construct a CategoryObject instance initialized with the specified
        /// parameters.
        /// </summary>
        /// <param name="categoryName">
        /// Friendly name to give the category.
        /// (Hint:Should be the same as the XACT Category name for consistancy)
        /// </param>
        /// <param name="audioEngine">
        /// AudioEngine instance required to initialize the XNA audio engine
        /// </param>
        public CategoryObject(string categoryName, AudioEngine audioEngine)
        {
            categoryObject = audioEngine.GetCategory(categoryName);
            this.Volume = categoryVolume;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Calculate and output the proper volume format used by the AudioCategory 
        /// SetVolume() method from the volume level enum specified.
        /// </summary>
        /// <param name="volumeLevel">Volume level enum</param>
        /// <returns>(float) Value of the converted volume level enum.</returns>
        protected float CalculateVolume(Volume volumeLevel)
        {
            // Return the clamped volume level value (Clamped to 0.0f - 1.0f)
            return MathHelper.Clamp((float)((int)volumeLevel / 100.0f), 0.0f, 1.0f);
        }

        /// <summary>
        /// Calculate and output the proper volume format to the AudioCategory volume
        /// enum format.
        /// </summary>
        /// <param name="volumeLevel">Volume level value</param>
        /// <returns>(Volume) Volume enum of the specified float volume</returns>
        protected Volume CalculateVolume(float volumeLevel)
        {
            // Get a working value to use to calculate the correct volume enum.
            int workVolume = (int)((float)volumeLevel * 100.0f + 1.0f);

            // Return the volume enum that best matches the specified volume level.
            if (volumeLevel >= 99)
                return Volume.Full;
            
            if (volumeLevel >= 66)
                return Volume.TwoThirds;
            
            if (volumeLevel >= 50)
                return Volume.Half;
            
            if (volumeLevel >= 33)
                return Volume.OneThird;
            
            return Volume.Mute;
        }

        /// <summary>
        /// Mute this category and all the audio cues that have been associated
        /// with it through the XACT tool.
        /// </summary>
        public void Mute()
        {
            // Process the current category state, and if the category is already 
            // muted then un-mute it and return the volume to the previously set 
            // volume value. Note that any volume changes that take place will override
            // the internal volume value and when the Mute method is called again to
            // un-mute a category this new volume value will be used.
            if (categoryMuted)
                categoryObject.SetVolume(0.0f);
            else
                categoryObject.SetVolume(MathHelper.Clamp((float)((int)categoryVolume / 100), 0.0f, 1.0f));

            // Toggle the current mute settings.
            categoryMuted = !categoryMuted;
                
        }

        /// <summary>
        /// Pause this category and all the audio cues that have been associated 
        /// with it through the XACT tool.
        /// </summary>
        public void Pause()
        {
            // Call the AudioCategory Pause method.
            categoryObject.Pause();
        }

        /// <summary>
        /// Resume this category from a previously paused state to an active state 
        /// and resume the playback of all the associated audio cues that were also
        /// paused.
        /// </summary>
        public void Resume()
        {
            // Call the AudioCategory Resume method.
            categoryObject.Resume();
        }

        /// <summary>
        /// Stop this category and all assocaiated audio cues from playing.
        /// </summary>
        public void Stop()
        {
            // Call the AudioCategory stop method with a default of Immediate.
            categoryObject.Stop(AudioStopOptions.Immediate);
        }

        /// <summary>
        /// Stop this category and all assocaiated audio cues from playing. Use
        /// the specified AudioStopOptions when stopping the audio playback of
        /// this category.
        /// </summary>
        public void Stop(AudioStopOptions stopOptions)
        {
            // Call the AudioCategory stop method with a parameter.
            categoryObject.Stop(stopOptions);
        }

        #endregion
    }
}
